#include "dvc_cmd_frame.h"
#include "pec_crc.h"
#include "string.h"

#define DVC_CMD_FRAME_DEBUG 1

#if DVC_CMD_FRAME_DEBUG
#include "stdio.h"
#define log_write printf
#else
#define log_write(x)
#endif

/* vc1006命令字, 直接使用PEC校验值, 从而加快运行速度 */
static const uint8_t dvc_cmd[10][4] = {
    {0x01, 0x00, 0x3e, 0x10}, /* 写入配置寄存器第 0 组指令 */
    {0x01, 0x01, 0xb5, 0x22}, /* 写入配置寄存器第 1 组指令 */
    {0x02, 0x00, 0x2d, 0xf6}, /* 读取状态寄存器第 0 组指令 */
    {0x02, 0x01, 0xa6, 0xc4}, /* 读取状态寄存器第 1 组指令 */
    {0x02, 0x02, 0xb0, 0xa0}, /* 读取状态寄存器第 2 组指令 */
    {0x02, 0x03, 0x3b, 0x92}, /* 读取状态寄存器第 3 组指令 */
    {0x03, 0x00, 0xa5, 0xba}, /* 读取配置寄存器第 0 组指令 */
    {0x03, 0x01, 0x2e, 0x88}, /* 读取配置寄存器第 1 组指令 */
    {0x04, 0x00, 0x0a, 0x3a}, /* 清除指令 */
    {0x04, 0x01, 0x81, 0x08}, /* 休眠指令 */
};

uint8_t dvc_pec_crc_check(uint8_t *buffer, uint8_t size)
{
    uint16_t crc;
    for (uint8_t j = 0; j < (size / 8); j++)
    {
        crc = pec_crc(buffer, 6);
        if ((crc >> 8) != buffer[6] || (crc & 0xFF) != buffer[7])
        {
            return j + 1;
        }
        buffer += 8;
    }
    return 0;
}

/**
 *  读取总线数据
 *  成功返回 0
 */
uint8_t dvc_frame_read(dvc_t dvc, dvc_cmd_t cmd, uint8_t *buffer, uint8_t size)
{
    if (!dvc || !buffer)
        return 1;

    if (cmd > dvc_cmd_max)
    {
        log_write("[FrameRead] cmd overflow\n");
        return 1;
    }

    if (!dvc->ops)
    {
        log_write("[FrameRead] no ops\n");
        return 1;
    }

    uint8_t send[8] = {0};
    memset(send, 0xFF, sizeof(send));
    size += 2;

    if (dvc->ops->read_reg)
    {
        if (dvc->ops->read_reg(dvc->ops->user_data, *(uint32_t *)&dvc_cmd[cmd], send, size))
        {
            log_write("[FrameRead] Failed to read cmd\n");
            return 1;
        }
    }
    else if (dvc->ops->write && dvc->ops->read)
    {
        if (dvc->ops->write(dvc->ops->user_data, (uint8_t *)&dvc_cmd[cmd], sizeof(dvc_cmd[0])))
        {
            log_write("[FrameRead] Failed to write cmd\n");
            return 1;
        }

        if (!dvc->ops->read(dvc->ops->user_data, send, size))
        {
            log_write("[FrameRead] Failed to read datas\n");
            return 1;
        }
    }
    else
    {
        log_write("[FrameRead] no ops\n");
        return 1;
    }

    uint8_t crc = dvc_pec_crc_check(send, size);
    if (crc)
    {
#if DVC_CMD_FRAME_DEBUG
        if ((dvc->in_read_check && dvc->retry_num >= dvc->retry_count) || !dvc->in_read_check)
        {
            log_write("[FrameRead] Crc Error : ");
            for (uint8_t i = 0; i < size; i++)
                log_write("0x%02X ", send[i]);
            log_write("\n");
        }
#endif
        return 1;
    }

    memcpy(buffer, send, size - 2);
    return 0;
}

/**
 *  写数据到总线
 *  成功返回 0
 */
uint8_t dvc_frame_write(dvc_t dvc, dvc_cmd_t cmd, uint8_t *buffer, uint8_t size)
{
    if (!dvc || !buffer)
        return 1;

    if (cmd > dvc_cmd_max)
    {
        log_write("[FrameRead] cmd overflow\n");
        return 1;
    }

    if (!dvc->ops)
    {
        log_write("[FrameRead] no ops\n");
        return 1;
    }

    uint8_t write_buf[8] = {0};
    if (size == 1) // 用于休眠和清除命令
    {
        write_buf[0] = *buffer;
    }
    else // 用于正常写入数据
    {
        for (uint8_t i = 0; i < size; i++)
        {
            write_buf[i] = buffer[i];
        }
        uint16_t crc = pec_crc(buffer, size);
        write_buf[size] = crc >> 8;
        write_buf[size + 1] = crc & 0xFF;
        size += 2;
    }

    if (dvc->ops->write_reg)
    {
        if (dvc->ops->write_reg(dvc->ops->user_data, *(uint32_t *)&dvc_cmd[cmd], write_buf, size))
        {
            log_write("[FrameRead] Failed to write cmd\n");
            return 1;
        }
    }
    else if (dvc->ops->write)
    {
        if (dvc->ops->write(dvc->ops->user_data, (uint8_t *)&dvc_cmd[cmd], sizeof(dvc_cmd[0])))
        {
            log_write("[FrameWrite] Failed to write cmd\n");
            return 1;
        }

        if (dvc->ops->write(dvc->ops->user_data, write_buf, size))
        {
            log_write("[FrameWrite] Failed to write data\n");
            return 1;
        }
    }
    else
    {
        log_write("[FrameRead] no ops\n");
        return 1;
    }

    return 0;
}

/**
 *  读取总线数据, 如果读取失败, 则重发指定次数
 *  成功返回 0
 */
uint8_t dvc_frame_read_check(dvc_t dvc, dvc_cmd_t cmd, uint8_t *buffer, uint8_t size)
{
    if (!dvc || !buffer)
        return 1;
#if DVC_CMD_FRAME_DEBUG
    dvc->in_read_check = 1;
#endif
    dvc->retry_num = 0;
    uint8_t i = 0;
    do
    {
        dvc->retry_num++;
        if (!dvc_frame_read(dvc, cmd, buffer, size))
        {
            return 0;
        }
        i++;
    } while (i < dvc->retry_count);
    log_write("[FrameReadCheck] Failed to read data\n");
    return 1;
}

/**
 *  写数据到总线, 如果写入失败, 则重发指定次数
 *  成功返回 0
 */
uint8_t dvc_frame_write_check(dvc_t dvc, dvc_cmd_t cmd, uint8_t *buffer, uint8_t size)
{
    if (!dvc || !buffer)
        return 1;
    uint8_t i = 0;
    for (; i < dvc->retry_count; i++)
    {
        if (!dvc_frame_write(dvc, cmd, buffer, size))
        {
            break;
        }
    }

    if (i >= dvc->retry_count)
    {
        log_write("[FrameWriteCheck] Failed to write data\n");
        return 1;
    }

    if (cmd == dvc_cmd_write_cfg0)
        cmd = dvc_cmd_read_cfg0;
    else if (cmd == dvc_cmd_write_cfg1)
        cmd = dvc_cmd_read_cfg1;

    uint8_t tmp[32] = {0};
    if (dvc_frame_read_check(dvc, cmd, tmp, size))
    {
        log_write("[FrameWriteCheck] Failed to read data\n");
        return 1;
    }

    for (i = 0; i < size; i++)
    {
        if (buffer[i] != tmp[i])
        {
#if DVC_CMD_FRAME_DEBUG
            log_write("[FrameWriteCheck] Data check failure :");
            log_write("\nwrite:");
            for (uint8_t j = 0; j < size; j++)
            {
                if (j && j % 16 == 0)
                    log_write("\n");
                log_write("0x%02X ", buffer[j]);
            }
            log_write("\nread:");
            for (uint8_t j = 0; j < size; j++)
            {
                if (j && j % 16 == 0)
                    log_write("\n");
                log_write("0x%02X ", tmp[j]);
            }
            log_write("\n");
#endif
            return 1;
        }
    }

    return 0;
}